#include <stdio.h>
// atoi函数
#include <stdlib.h>
// wait函数
#include <sys/wait.h>
// I/O函数
#include <unistd.h>
#include <time.h>
// socket 系列函数
#include <sys/socket.h>
// sockaddr_in 结构体
#include <netinet/in.h>
// hton 函数
#include <arpa/inet.h>
#include <signal.h>
// memset函数
#include <string.h>
// pthread库
#include <pthread.h>
// errno
#include <errno.h>
// fcntl函数
#include <fcntl.h>
#include <sys/types.h>
#include <sys/time.h>
// umask
#include <sys/stat.h>
// syslog系列函数以及宏
#include <syslog.h>
// ctrl + c时关掉服务器socket
void siginit_handle(int);
// 初始化指定数目的线程处理连接
void child_thread_init(int);
// 客户端链表节点结构体
struct client_fd
{
	int fd;
	struct client_fd* pre;
	struct client_fd* next;
};
typedef struct client_fd client_fd;
// 链表
client_fd* client_fd_link = NULL;
// 链表互斥锁
pthread_mutex_t link_mutex;
// 服务器socket
int server_fd = 0;
// 添加链表元素
int add_client(int);
// 删除链表元素，通过fd删除元素
int del_client_by_fd(int);
// 通过节点地址删除元素
int del_client_by_client(client_fd*);
// 服务器连接日志
void server_log(struct sockaddr_in*);
// ctrl + c关闭服务器socket
void sigint_handle(int sig);
// 把链表中的fd加入到set中，并返回socket描述符最大的一个
int init_set(fd_set*);
int main(int argc,char* argv[])
{
	if(argc < 2)
	{
		printf("incorrect parameter\n");
		return 1;
	}
	if(SIG_ERR == signal(SIGINT,sigint_handle))
	{
		perror("signal error");
		return 1;
	}
	/* 守护进程变成步骤  */
	// 1. 使用umask将文件模式创建屏蔽字设置为0
	umask(0);
	// 2. 调用fork，然后让父进程退出
	int pid = fork();
	if(pid > 0)
	{
		exit(0);
	}
	// 3. 调用setsid创建一个新会话
	setsid();
	// 4. 将当前工作目录更改为根目录
	chroot("/");
	// 5. 关闭不需要的文件描述符
	close(STDIN_FILENO);
	close(STDOUT_FILENO);
	close(STDERR_FILENO);
	
	/* 使用syslog */
	// 1. 打开
	openlog("jin",LOG_PID,LOG_SYSLOG);
	// 2. 记录
	syslog(LOG_INFO,"jin deamon start");
	// 3. 关闭
	closelog();
	
	// 初始化互斥锁
	pthread_mutex_init(&link_mutex,NULL);
	struct sockaddr_in server_attr;
	memset(&server_attr,0,sizeof(server_attr));
	/*
 	 * 1. 创建socket
	 * socket创建在内核中，是一个结构体
	 * AF_INET : IPV4
	 * SOCK_STREAM : TCP协议
	 */
	server_fd = socket(AF_INET,SOCK_STREAM,0);
	if(server_fd == -1)
	{
		perror("sock error");
		return 1;
	}
	
	/*
 	 * 2. 设置SO_REUSEADDR，避免重启导致bind报错
 	 */
	int opt = 1;
	setsockopt(server_fd,SOL_SOCKET,SO_REUSEADDR,(void*)&opt,sizeof(int));
	/*
 	 * 3. 调用bind函数将socket与地址(ip,port)进行绑定
 	 */
	server_attr.sin_family = AF_INET;
	// 转换位网络字节序
	server_attr.sin_port = htons((short)atoi(argv[1]));
	// 表示所有ip，也可以指定第一个ip，指定ip时要把主机字节序转换成网络字节序
	server_attr.sin_addr.s_addr = INADDR_ANY;
	// 绑定ip端口
	if(-1 == bind(server_fd,(struct sockaddr*)&server_attr,sizeof(server_attr)))
	{
		perror("bind error");
		return 1;
	}
	/*
 	 * 4. 调用listen函数启动监听，通知系统接收来自客户端的请求
 	 * 第二个参数代表客户端队列的长度
 	 * 执行成功后能用netstat查看
 	 */
	if(-1 == listen(server_fd,8))
	{
		perror("listen error");
		return 1;
	}
	
	// 忽略SIGPIPE信号处理函数，避免由于客户端断开连接，并由于read发送SIGPIPE信号导致进程结束
	signal(SIGPIPE,SIG_IGN);
	int client_fd = 0;
	struct sockaddr_in client_attr;
	socklen_t addrlen = sizeof(client_attr);
	// 开启子线程处理连接·
	child_thread_init(8);
	while(1)
	{
		/*
	 	* 5. 调用accept获得客户端连接，并返回一个新的socket文件描述符，新的文件描述符放进client_fd链表中
	 	* 没有客户端连接，此函数会阻塞，直到获得一个客户端连接
  	 	*/	
		client_fd = accept(server_fd,(struct sockaddr*)&client_attr,&addrlen);
		if(client_fd <= 0)
		{
			continue;
		}
		server_log(&client_attr);
		add_client(client_fd);
		// 线程是共享的进程资源，这里不要关闭，不然线程里面的也没了
	}
	return 0;
}
void sigint_handle(int sig)
{
	close(server_fd);
	exit(0);
}
void server_log(struct sockaddr_in* client_attr)
{
	char ip[16] = {'\0'};
	// 结构体的的数据不能直接显示，需要先转换成本机字节序
	inet_ntop(AF_INET,&client_attr->sin_addr.s_addr,ip,sizeof(ip));
	printf("connected by %s:%d\n",ip,ntohs(client_attr->sin_port));
}
void response(client_fd* temp)
{
	char res[512] = {'\0'},buf[512] = {'\0'};
	int read_len = 0,res_len = sizeof(res);
	read_len = read(temp->fd,res,res_len);
	// 客户端关闭，则关闭客户端连接
	if(read_len == 0)
	{
		printf("close connection\n");
		del_client_by_client(temp);
		return;
	}
	else if(read_len > 0)
	{
		// buf 与 res不能一样
		sprintf(buf,"thread : %lu\nmessage : %s\n",pthread_self(),res);
	
		sprintf(res,"%s\n",buf);
		
		if(write(temp->fd,res,res_len) != res_len)
		{
			// 客户端关闭，则关闭客户端连接
			if(errno == EPIPE)
			{
				printf("close connection\n");
				del_client_by_client(temp);
				return;
			}
		}
	}
}
void* thread_handle(void* data)
{
	/*
  	* 5. 遍历客户端socket_fd，对有输入的客户端进行回应
  	*/
	client_fd* temp;
	fd_set set;
	int max = init_set(&set);
	struct timeval time = {2,0};
	int n = 0;
	while(1)
	{
		// printf("n : %d;max : %d\n",n,max);
		// 注意:是文件描述符最大的加1，
		n = select(max + 1,&set,NULL,NULL,&time);
		temp = client_fd_link;
		while(temp != NULL && n > 0)
		{
			if(FD_ISSET(temp->fd,&set) == 0)
			{
				continue;
			}
			response(temp);
			temp = temp->next;
			n--;
		}
		// 时间需要重新设置
		time.tv_sec = 2;
		time.tv_usec = 0;
		// 用最新的连接信息设置set，重设最大值
		max = init_set(&set);
	}
	return (void*)NULL;
}
void child_thread_init(int max)
{
	pthread_t tid = 0;
	// 初始化线程属性，用来创建分离的线程，使子线程结束后自动回收
	pthread_attr_t thread_attr;
	pthread_attr_init(&thread_attr);
	pthread_attr_setdetachstate(&thread_attr,PTHREAD_CREATE_DETACHED);
	// 子线程处理I/O，达到并发的效果
	while(max > 0)
	{
		pthread_create(&tid,&thread_attr,thread_handle,(void*)NULL);
		max--;
	}
}
// 添加链表元素
int add_client(int fd)
{
	pthread_mutex_lock(&link_mutex);
	if(client_fd_link == NULL)
	{
		client_fd_link = malloc(sizeof(client_fd));
		client_fd_link->pre = NULL;
		client_fd_link->next = NULL;
		client_fd_link->fd = fd;
		pthread_mutex_unlock(&link_mutex);
		return 0;
	}
	client_fd* temp = client_fd_link;
	while(temp->next != NULL)
	{
		temp = temp->next;
	}
	temp->next = malloc(sizeof(client_fd));
	temp->next->pre = temp;
	temp->next->next = NULL;
	temp->next->fd = fd;
	pthread_mutex_unlock(&link_mutex);
	return 0;
}
// 删除链表元素，通过fd删除元素
int del_client_by_fd(int fd)
{
	pthread_mutex_lock(&link_mutex);
	if(client_fd_link == NULL)
	{
		pthread_mutex_unlock(&link_mutex);
		return -1;
	}
	client_fd* temp = client_fd_link->next;
	// 链表第一个元素比较特殊，做特殊处理
	if(fd == client_fd_link->fd)
	{
		free(client_fd_link);
		client_fd_link = temp;
		if(client_fd_link != NULL)
		{
			client_fd_link->pre = NULL;
		}
		pthread_mutex_unlock(&link_mutex);
		return 0;
	}
	while(temp != NULL)
	{
		if(temp->fd == fd)
		{
			temp->pre->next = temp->next;
			if(temp->next != NULL)
			{
				temp->next->pre = temp->pre;
			}
			close(temp->fd);
			free(temp);
			pthread_mutex_unlock(&link_mutex);
			return 0;
		}
		temp = temp->next;
	}
	pthread_mutex_unlock(&link_mutex);
	return -1;
}
// 通过节点地址删除元素
int del_client_by_client(client_fd* client)
{
	pthread_mutex_lock(&link_mutex);
	if(client_fd_link == NULL)
	{
		pthread_mutex_unlock(&link_mutex);
		return -1;
	}
	client_fd* temp = client_fd_link->next;
	// 链表第一个元素比较特殊，做特殊处理
	if(client == client_fd_link)
	{
		free(client_fd_link);
		client_fd_link = temp;
		if(client_fd_link != NULL)
		{
			client_fd_link->pre = NULL;
		}
		pthread_mutex_unlock(&link_mutex);
		return 0;
	}
	while(temp != NULL)
	{
		if(temp == client)
		{
			temp->pre->next = temp->next;
			if(temp->next != NULL)
			{
				temp->next->pre = temp->pre;
			}
			close(temp->fd);
			free(temp);
			pthread_mutex_unlock(&link_mutex);
			return 0;
		}
		temp = temp->next;
	}
	pthread_mutex_unlock(&link_mutex);
	return -1;
}
int init_set(fd_set* set)
{
	client_fd* temp = client_fd_link;
	// 清空set
	FD_ZERO(set);
	int max = 0;
	while(temp != NULL)
	{
		// 获取最大的set
		if(max < temp->fd)
		{
			max = temp->fd;
		}
		// 把client_fd加入set中
		FD_SET(temp->fd,set);
		temp = temp->next;
	}
	return max;
}
